%%%-------------------------------------------------------------------
%%% @doc Handles fetching packages from the remote repository and 
%%%      placing them in the erlware repo.
%%% @end
%%%-------------------------------------------------------------------
-module(fax_install).

%%--------------------------------------------------------------------
%% Include files
%%--------------------------------------------------------------------
-include("faxien.hrl").

%%--------------------------------------------------------------------
%% External exports
%%--------------------------------------------------------------------
-export([
	 install_remote_application/5,
	 install_remote_application/4,
	 install_remote_application/3,
	 install_remote_release/5,
	 install_remote_release/4,
	 install_remote_erts/3,
	 install_application/3,
	 install_erts/3,
	 install_release/4
	]).

%%--------------------------------------------------------------------
%% Macros
%%--------------------------------------------------------------------
-define(FETCH_TIMEOUT, infinity).

%%--------------------------------------------------------------------
%% Records
%%--------------------------------------------------------------------

%%====================================================================
%% External functions
%%====================================================================

%%--------------------------------------------------------------------
%% @doc 
%%  Install an application package.  This function will determine whether the target (AppNameOrPath) is a request to install
%%  an application from a remote repository or to install a tared up release package (.tar.gz) or an untarred package directory.
%% @spec install_application(Repos, AppNameOrPath, InstallationPath) -> ok | {error, Reason} | exit()
%% where
%%     Type = application | release
%%     AppNameOrPath = string()
%%     InstallationPath = string()
%% @end
%%--------------------------------------------------------------------
install_application(Repos, AppNameOrPath, InstallationPath) ->
    case filelib:is_file(AppNameOrPath) of
	true  -> fax_local_install:install_application(AppNameOrPath, InstallationPath);
	false -> install_remote_application(Repos, AppNameOrPath, InstallationPath)
    end.
	    

%%--------------------------------------------------------------------
%% @todo refactor this function to have a more direct instance that allows me to indicate the full url to install from.  Also as
%% part of this refactor the find_highest_vsn function to return the exact URL it finds the highest version in. 
%% @doc 
%%  Install an application package from a repository. Versions can be the string "LATEST". Calling this function will install 
%%  a remote application at IntallationPath/lib/Appname-Appvsn.
%% <pre>
%% Examples:
%%  install_remote_application(["http"//repo.erlware.org/pub"], "5.5.5", gas, "4.6.0", "/usr/local/erlware")
%%  install_remote_application(["http"//repo.erlware.org/pub"], "5.5.5", gas, "LATEST", "/usr/local/erlware")
%% </pre>
%% @spec install_remote_application(Repos, TargetErtsVsn, AppName, AppVsn, InstallationPath) -> ok | {error, Reason} | exit()
%% where
%%     Repos = string()
%%     TargetErtsVsn = string()
%%     AppName = string()
%%     AppVsn = string() 
%%     InstallationPath = string()
%% @end
%%--------------------------------------------------------------------
install_remote_application([], _TargetErtsVsn, _AppName, "LATEST", _InstallationPath) ->
    {error, installation_failed};
install_remote_application(Repos, TargetErtsVsn, AppName, "LATEST", InstallationPath) ->
    {ok, {Repo, HighVsn}} = fax_util:find_highest_vsn(Repos, TargetErtsVsn, AppName, lib),
    ShortenedRepos = lists:delete(Repo, Repos),
    case catch install_remote_application([Repo|ShortenedRepos], TargetErtsVsn, AppName, HighVsn, InstallationPath) of
	ok -> 
	    ok;
	{error, already_installed} = Error ->
	    Error;
	Error -> 
	    ?ERROR_MSG("install remote application failed with ~p for vsn ~p in repo ~p moving on to next repo~n", [Error, HighVsn, Repo]), 
	    install_remote_application(ShortenedRepos, TargetErtsVsn, AppName, "LATEST", InstallationPath)
    end;
install_remote_application(Repos, TargetErtsVsn, AppName, AppVsn, InstallationPath) ->
    ?INFO_MSG("install_remote_application(~p, ~p, ~p, ~p, ~p)~n", [Repos, TargetErtsVsn, AppName, AppVsn, InstallationPath]),
    AppDir = fax_paths:installed_app_path(InstallationPath, AppName, AppVsn),
    case fax_validation:is_package_an_app(AppDir) of
	false -> 
	    ok                  = ewl_file:delete_dir(AppDir),
	    {ok, TmpPackageDir} = fax_util:create_tmp_package_dir(),
	    ok = fax_util:foreach_erts_vsn(TargetErtsVsn, 
				  fun(ErtsVsn_) -> 
					  ewr_fetch:fetch_binary_package(Repos, ErtsVsn_, AppName, AppVsn, 
									 TmpPackageDir, ?FETCH_TIMEOUT)
				  end),
            ok = fax_local_install:install_application(lists:flatten([TmpPackageDir, AppName, "-", AppVsn]), InstallationPath),
	    ok = ewl_file:delete_dir(TmpPackageDir);
	true -> 
	    fax_local_install:overwrite_yes_no(
	      fun() -> install_remote_application(Repos, TargetErtsVsn, AppName, AppVsn, InstallationPath) end,  
	      fun() -> {error, already_installed} end, 
	      AppDir)
    end.

%% @equiv install_remote_application(Repos, TargetErtsVsn, AppName, AppVsn, InstallationPath)
%% @spec install_remote_application(Repos, AppName, AppVsn, InstallationPath) -> ok | {error, Reason} | exit()
install_remote_application(Repos, AppName, AppVsn, InstallationPath) ->
    TargetErtsVsn = ewr_util:erts_version(),
    install_remote_application(Repos, TargetErtsVsn, AppName, AppVsn, InstallationPath).

%% @equiv install_remote_application(Repos, TargetErtsVsn, AppName, "LATEST", InstallationPath)
%% @spec install_remote_application(Repos, AppName, InstallationPath) -> ok | {error, Reason} | exit()
install_remote_application(Repos, AppName, InstallationPath) ->
    TargetErtsVsn = ewr_util:erts_version(),
    install_remote_application(Repos, TargetErtsVsn, AppName, "LATEST", InstallationPath).
			
%%--------------------------------------------------------------------
%% @doc 
%%  Install an application package.  This function will determine whether the target (AppNameOrPath) is a request to install
%%  an application from a remote repository or to install a tared up release package (.tar.gz) or an untarred package directory.
%% @spec install_application(Repos, AppNameOrPath, InstallationPath) -> ok | {error, Reason} | exit()
%% where
%%     Type = application | release
%%     AppNameOrPath = string()
%%     InstallationPath = string()
%% @end
%%--------------------------------------------------------------------
install_erts(Repos, ErtsVsnOrPath, InstallationPath) ->
    case filelib:is_file(ErtsVsnOrPath) of
	true  -> fax_local_install:install_erts(ErtsVsnOrPath, InstallationPath);
	false -> install_remote_erts(Repos, ErtsVsnOrPath, InstallationPath)
    end.
	    
%%--------------------------------------------------------------------
%% @doc 
%%  Install an erts package from a repository. 
%% <pre>
%% Examples:
%%  install_remote_erts(["http"//repo.erlware.org/pub"], "5.5.5", "5.5.5", "/usr/local/erlware")
%% </pre>
%% @spec install_remote_erts(Repos, TargetErtsVsn, ErtsName, ErtsVsn, InstallationPath) -> ok | {error, Reason} | exit()
%% where
%%     Repos = string()
%%     TargetErtsVsn = string()
%%     ErtsName = string()
%%     ErtsVsn = string() 
%%     InstallationPath = string()
%% @end
%%--------------------------------------------------------------------
install_remote_erts(Repos, ErtsVsn, InstallationPath) ->
    ?INFO_MSG("install_remote_erts(~p, ~p, ~p)~n", [Repos, ErtsVsn, InstallationPath]),
    ErtsDir = fax_paths:installed_erts_path(InstallationPath, ErtsVsn),
    case fax_validation:is_package_erts(ErtsDir) of
	false -> 
	    ok  = ewl_file:delete_dir(ErtsDir),
	    {ok, TmpPackageDir} = fax_util:create_tmp_package_dir(),
	    ok  = ewr_fetch:fetch_erts_package(Repos, ErtsVsn, TmpPackageDir, ?FETCH_TIMEOUT),
            Res = fax_local_install:install_erts(lists:flatten([TmpPackageDir, "erts/", ErtsVsn]), InstallationPath),
	    ok  = ewl_file:delete_dir(TmpPackageDir),
	    Res;
	true -> 
	    ok
    end.

%%--------------------------------------------------------------------
%% @doc 
%%  Install a release package.  This function will determine whether the target (AppNameOrPath) is a request to install
%%  an application from a remote repository or to install a tared up release package (.tar.gz) or an untarred package directory.
%% @spec install_release(Repos, AppNameOrPath, InstallationPath, IsLocalBoot) -> ok | {error, Reason} | exit()
%% where
%%     Type = application | release
%%     AppNameOrPath = string()
%%     InstallationPath = string()
%%     IsLocalBoot = bool()
%% @end
%%--------------------------------------------------------------------
install_release(Repos, ReleaseNameOrPath, InstallationPath, IsLocalBoot) ->
    ?INFO_MSG("fax_install:install_release(~p, ~p, ~p, ~p)~n", [Repos, ReleaseNameOrPath, InstallationPath, IsLocalBoot]),
    case filelib:is_file(ReleaseNameOrPath) of
	true  -> fax_local_install:install_release(Repos, ReleaseNameOrPath, InstallationPath, IsLocalBoot);
	false -> install_remote_release(Repos, ReleaseNameOrPath, InstallationPath, IsLocalBoot)
    end.
    
%%--------------------------------------------------------------------
%% @doc 
%%  Install a release package from a repository. Versions can be the string "LATEST"
%% @spec install_remote_release(Repos, RelName, RelVsn, InstallationPath, IsLocalBoot) -> ok | {error, Reason} | exit()
%% where
%%     Repos = string()
%%     RelName = string()
%%     RelVsn = string() 
%%     InstallationPath = string()
%%     IsLocalBoot = bool()
%% @end
%%--------------------------------------------------------------------
install_remote_release([], _RelName, "LATEST", _InstallationPath, _IsLocalBoot) ->
    {error, installation_failed};
install_remote_release(Repos, RelName, "LATEST", InstallationPath, IsLocalBoot) ->
    {ok, {Repo, HighVsn}} = fax_util:find_highest_vsn(Repos, RelName, releases),
    %% In the next line I make sure the repo containing the highest version found is in the first position.
    ShortenedRepos = lists:delete(Repo, Repos),
    case catch install_remote_release([Repo|ShortenedRepos], RelName, HighVsn, InstallationPath, IsLocalBoot) of
	ok -> 
	    ok;
	{error, already_installed} = Error ->
	    Error;
	Error -> 
	    ?ERROR_MSG("install_remote_release for the latest version, ~s, of ~s resulted in ~p~n", [HighVsn, RelName, Error]), 
	    io:format("~n *ERROR* Failure to install release ~s-~s~n", [RelName, HighVsn]),
	    install_remote_release(ShortenedRepos, RelName, "LATEST", InstallationPath, IsLocalBoot)
    end;
install_remote_release(Repos, RelName, RelVsn, InstallationPath, IsLocalBoot) ->
    ReleaseDir = fax_paths:installed_release_path(InstallationPath, RelName, RelVsn),
    io:format("~nInitiating Install for Remote Release ~s-~s~n", [RelName, RelVsn]),
    case fax_validation:is_package_a_release(ReleaseDir) of
	false -> 
	    ok = ewl_file:delete_dir(ReleaseDir),
	    {ok, TmpPackageDir} = fax_util:create_tmp_package_dir(),
	    ?INFO_MSG("install_remote_release/4 calling ewr_fetch:fetch_release_package(~p, ~p, ~p, ~p, ~p)~n", 
				  [Repos, RelName, RelVsn, TmpPackageDir, IsLocalBoot]),
	    ok = ewr_fetch:fetch_release_package(Repos, RelName, RelVsn, TmpPackageDir),
            ok = fax_local_install:install_release(Repos, lists:flatten([TmpPackageDir, RelName, "-", RelVsn]), 
						   InstallationPath, IsLocalBoot),
	    ok = ewl_file:delete_dir(TmpPackageDir);
	true -> 
	    io:format("~p is already present~n", [ReleaseDir]), 
	    fax_local_install:overwrite_yes_no(fun() -> install_remote_release(Repos, RelName, RelVsn, InstallationPath, IsLocalBoot) end,  
			     fun() -> {error, already_installed} end, 
			     ReleaseDir)
    end.

%% @equiv install_remote_release(Repos, RelName, "LATEST", InstallationPath, IsLocalBoot)
%% @spec install_remote_release(Repos, RelName, InstallationPath, IsLocalBoot) -> ok | {error, Reason} | exit()
install_remote_release(Repos, RelName, InstallationPath, IsLocalBoot) ->
    install_remote_release(Repos, RelName, "LATEST", InstallationPath, IsLocalBoot).


%%====================================================================
%% Internal functions
%%====================================================================

